use utf8;
use warnings;
use 5.24.0;
use feature ':5.24';
use experimentals;
use Data::Dumper qw(Dumper);
use Carp qw(carp croak confess);
use Cwd;
use IO::Handle;
use List::MoreUtils qw( last_value );

BEGIN{push @INC, './';}

use STDF_inline_C;

use constant {
    HEAD_NUM_GROSS=>255,
};

my %header_info        = ();
my @wafer_record       = ();
my %sbin_defination    = ();
my %hbin_defination    = ();
my @parametric_record  = ();
my @bin_result_record  = ();
my @tsr_rec_collection = ();

my @DTR_collection = ();

my $test_parameters     = {};
my $die_data_collection = {};
my $test_information    = {};
my $pin_map             = {};
my $current_wafer_id    = undef;

my $unit_prefix = {
    0   => '',
    3   => 'm',
    6   => 'u',
    9   => 'n',
    10  => 'A',
    12  => 'p',
    -3  => 'K',
    -6  => 'M',
    -9  => 'G',
    -12 => 'T',
};
my $eps_flag = 0;

my $die_array_per_section = {};

sub get_argv_hash{
    my %param_hash=();
    if(not defined $ARGV[0] or not defined $ARGV[1]){
        #如果没有给出第一个参数，则返回undef。
        return undef;
    }
    $param_hash{'input_file'}=shift @ARGV;
    $param_hash{'output_file'}=shift @ARGV;
    if( '-output_pf' ~~ @ARGV ){
        $param_hash{ 'output_pf' } = 1;
    }
    return \%param_hash;
}

sub handle_wir( $rec_hash ){    
    $current_wafer_id = $rec_hash->{ WAFER_ID };
    say "Current wafer id is $current_wafer_id";
}

sub handle_mir( $rec_hash ){
    $test_information = $rec_hash;
}

sub handle_sdr( $rec_hash ){
    $test_information = {
        %$test_information,
        %$rec_hash
    };
}

sub handle_pmr( $rec_hash ){
    $pin_map->{ $rec_hash->{ CHAN_NAM } } = {
        LOG_NAM  => $rec_hash->{ LOG_NAM  },
        PMR_INDX => $rec_hash->{ PMR_INDX },
        SITE_NUM => $rec_hash->{ SITE_NUM },
    }
}

sub sequence_generate( $category ){
    state $seq_per_category = {};
    my $current_seq = $seq_per_category->{ $category } // 0;
    $seq_per_category->{ $category } ++;
    return $current_seq;
}

sub add_parameter( $par_info ){
    $par_info->{ PAR_NAME } = param_name_sanitize( 
                                                    $par_info->{ PAR_NAME },
                                                    $par_info->{ HEAD_NUM },
                                                    $par_info->{ SITE_NUM },
                                                    );
    my $key = $par_info->{ TEST_NUM } . '_' . $par_info->{ PAR_NAME };
    if( not exists $test_parameters->{ $key } ){
        my $list_index = sequence_generate( 'PAR' );
        $par_info->{ UNITS } = ( $unit_prefix->{ $par_info->{ SCALE } // 0 } // '' ) . ( $par_info->{ UNITS } // '' );
        if( $par_info->{ SCALE } ){
            $par_info->{ LO_SPEC } = $par_info->{ LO_SPEC } * ( 10 ** $par_info->{ SCALE } );
            $par_info->{ HI_SPEC } = $par_info->{ HI_SPEC } * ( 10 ** $par_info->{ SCALE } );
        }

        $test_parameters->{ $key } = {
                LIST_INDEX => $list_index,
                %$par_info,
            };
        
        return $list_index, $key;
    }else{
        return $test_parameters->{ $key }->{ LIST_INDEX }, $key;
    }
}

sub handle_ptr( $rec_hash ){
    #处理 参数名里的channel name
    # $rec_hash->{ TEST_TXT } = param_name_sanitize( 
    #     $rec_hash->{ TEST_TXT },
    #     $rec_hash->{ HEAD_NUM },
    #     $rec_hash->{ SITE_NUM },
    #     );
    $rec_hash->{ TEST_TXT } = 'PARAM_' . $rec_hash->{ TEST_TXT };
    my ( $list_index, $par_key ) = add_parameter( {
        TEST_NUM => $rec_hash->{ TEST_NUM },
        PAR_NAME => $rec_hash->{ TEST_TXT },
        LO_SPEC  => $rec_hash->{ LO_LIMIT  },
        HI_SPEC  => $rec_hash->{ HI_LIMIT  },
        UNITS    => $rec_hash->{ UNITS    },
        SCALE    => $rec_hash->{ RES_SCAL } // 0,
        HEAD_NUM => $rec_hash->{ HEAD_NUM },
        SITE_NUM => $rec_hash->{ SITE_NUM },
    } );
    my $scale_value = $test_parameters->{ $par_key }->{ SCALE } // 0;
    $rec_hash->{ RESULT } = $rec_hash->{ RESULT } * ( 10 ** $scale_value );
    my $pf = undef;
    
    if( ( $rec_hash->{ TEST_FLG } & 64 ) == 0 ){
        if( $rec_hash->{ TEST_FLG } == 0 ){
            $pf = 1;
        }else{
            $pf = 0;
        }
    }

    my ( $head_num, $site_num, $result ) = @$rec_hash{ qw( HEAD_NUM SITE_NUM RESULT ) };
    $die_array_per_section->{ $head_num }{ $site_num }{ result }->[ $list_index ] = $result;
    $die_array_per_section->{ $head_num }{ $site_num }{ pf }->[ $list_index ] = $pf;
    #$die_array_per_section->{ $head_num }{ $site_num }{ WAFER_ID } = $current_wafer_id // '';
}

sub param_name_sanitize( $par_name, $head_num, $site_num ){
    state $memory_hash = {};
    if( exists $memory_hash->{ $par_name } ){
        return $memory_hash->{ $par_name };
    }
    my $return_par_name = $par_name;
    if( $par_name =~ /^
        (?<test_name1>.+?\s+)
        (?<chan_name>\S+)
        (?<test_name2>\s+<\s*>.+)*$/ismx 
    ){
        my $chan_name = $+{ chan_name };
        my $pin_name = $pin_map->{ $chan_name }{ LOG_NAM };

        if( defined $pin_name ){
             $return_par_name = sprintf( '%s%s%s', $+{ test_name1 }, $pin_name, ( $+{ test_name2 } // '' ) );
        }
    }
    $memory_hash->{ $par_name } = $return_par_name;
    return $return_par_name;
}

sub handle_ftr( $rec_hash ){
    my ( $list_index ) = add_parameter( {
        TEST_NUM => $rec_hash->{ TEST_NUM },
        PAR_NAME => 'FUNC_' . $rec_hash->{ TEST_TXT },
        VECT_NAM => $rec_hash->{ VECT_NAM },
        HEAD_NUM => $rec_hash->{ HEAD_NUM },
        SITE_NUM => $rec_hash->{ SITE_NUM },
    } );
    my ( $head_num, $site_num, $result ) = @$rec_hash{ qw( HEAD_NUM SITE_NUM TEST_FLG ) };
    if( ( $result & 192 ) != 0 ){
        $result = 0;
    }else{
        $result = 1;
    }

    $die_array_per_section->{ $head_num }{ $site_num }{ result }->[ $list_index ] = $result;
    $die_array_per_section->{ $head_num }{ $site_num }{ pf }->[ $list_index ] = $result;
}

sub handle_mpr( $rec_hash ){
    return if( $rec_hash->{ RSLT_CNT } == 0 );
    state $pmr_idx_pin_name_map = {
        map { ( $_->{ PMR_INDX }, $_->{ LOG_NAM } // '' ) } values $pin_map->%*
    };
    my ( $head_num, $site_num, $test_num, $test_name, $lo_spec, $hi_spec, $units, $scale ) = @$rec_hash{ qw( HEAD_NUM SITE_NUM TEST_NUM TEST_TXT LO_LIMIT HI_LIMIT UNITS_IN RES_SCAL ) };
    # my $unknown_pmr_idx_count = 1;
    # my @pin_names = map { $pmr_idx_pin_name_map->{ $_ } // 'unknown' . $unknown_pmr_idx_count++ } $rec_hash->{ RTN_INDX }->@*;

    my $pf = undef;
    if( $rec_hash->{ TEST_FLG } & 64 == 0 ){
        if( $rec_hash->{ TEST_FLG } == 0 ){
            $pf = 1;
        }else{
            $pf = 0;
        }
    }

    foreach my $val_idx ( 0..$rec_hash->{ RSLT_CNT } - 1 ){
        my $new_test_name = ( $test_name . '[' . ( $val_idx + 1) ) . ']';

        my $result = $rec_hash->{ RTN_RSLT }->[ $val_idx ];
        my ( $pmr_result_index ) = add_parameter({
            TEST_NUM => $test_num,
            PAR_NAME => 'PARAM_' . $test_name,
            LO_SPEC  => $lo_spec,
            HI_SPEC  => $hi_spec,
            UNITS    => $units,
            HEAD_NUM => $head_num,
            SITE_NUM => $site_num,
            SCALE    => $scale
        });
        $die_array_per_section->{ $head_num }{ $site_num }->{ result }->[ $pmr_result_index ] = $result;
        $die_array_per_section->{ $head_num }{ $site_num }->{ pf }->[ $pmr_result_index ] = $pf;
    }
}

sub handle_bps( $rec_hash ){
    if(not %$die_array_per_section ){
        return;
    }
    my $td_index = sequence_generate( 'TD' );
    push @DTR_collection, "CurrentTDIndex=$td_index";
    foreach my $head_num ( keys %$die_array_per_section ){
        foreach my $site_num ( keys %{ $die_array_per_section->{ $head_num } } ){
            my $die_hash = $die_array_per_section->{ $head_num }{ $site_num };
            my ( $part_id, $hard_bin, $soft_bin, $diex, $diey, $wafer_id, $test_t ) = @$die_hash{ qw( PART_ID HARD_BIN SOFT_BIN DIEX DIEY WAFER_ID TEST_T )  };
            my $die_seq = sequence_generate('DIE');
            $die_data_collection->{ $die_seq } = {
                HEAD_NUM => $head_num,
                SITE_NUM => $site_num,
                TEST_SEQ => $die_seq,
                PART_ID  => $part_id,
                HARD_BIN => $hard_bin,
                SOFT_BIN => $soft_bin,
                DIEX     => $diex,
                DIEY     => $diey,
                WAFER_ID => $wafer_id,
                RESULT   => $die_hash->{ result },
                PF       => $die_hash->{ pf },
                TEST_T   => $test_t,
                TD_INDEX => $td_index,
            };
        }
    }
    $die_array_per_section = {};
}

sub handle_prr( $rec_hash ){
    if( not %$die_array_per_section ){
        warn sprintf( "Get PRR Rec, but no die test information. PRR Part_Id is %d", $rec_hash->{ PART_ID } ) ;
    }

    my ( $head_num, $site_num, $part_id, $hard_bin, $soft_bin, $diex, $diey, $test_t ) = @$rec_hash{ qw( HEAD_NUM SITE_NUM PART_ID HARD_BIN SOFT_BIN X_COORD Y_COORD TEST_T ) };
    if( not defined $die_array_per_section->{ $head_num }{ $site_num } ){
        $die_array_per_section->{ $head_num }{ $site_num } = {};
    }
    my $die_hash = $die_array_per_section->{ $head_num }{ $site_num };
    $die_hash->{ PART_ID } = $part_id;
    $die_hash->{ HARD_BIN } = $hard_bin;
    $die_hash->{ SOFT_BIN } = $soft_bin;
    $die_hash->{ DIEX     } = $diex == -32768 ? undef : $diex;
    $die_hash->{ DIEY     } = $diey == -32768 ? undef : $diey;
    $die_hash->{ TEST_T   } = $test_t;
    $die_hash->{ WAFER_ID } = $current_wafer_id // '';

}

sub handle_sbr( $rec_hash ){
    #if($sbin_defination
    my ( $sb_no, $pf, $name ) = @$rec_hash{ qw( SBIN_NUM SBIN_PF SBIN_NAM ) };
    return if( defined $sbin_defination{ $sb_no } );
    
    $sbin_defination{ $sb_no } = {
        SBIN_PF   => $pf,
        SBIN_NAME => $name // '',
    };
}

sub handle_hbr( $rec_hash ){
    my ( $hb_no, $pf, $name ) = @$rec_hash{ qw( HBIN_NUM HBIN_PF HBIN_NAM ) };
    return if( defined $hbin_defination{ $hb_no } );

    $hbin_defination{ $hb_no } = {
        HBIN_PF   => $pf,
        HBIN_NAME => $name // '',
    };
}

sub handle_mrr( $rec_hash ){
     $test_information = {
        %$test_information,
        %$rec_hash
    };
}

sub handle_tsr( $rec_hash ){
    #only handle summary TSR record
    return if( not( $rec_hash->{ HEAD_NUM } == 255 and $rec_hash->{ SITE_NUM } == 255 ));
    my @item_invalid = map { vec( pack('C',$rec_hash->{ OPT_FLAG }), $_,1 ) } 0..7;
    @item_invalid = @item_invalid[ ( 0,1,2,4,5, ) ];
    my @keys_list = qw( TEST_MIN TEST_MAX TEST_TIM TST_SUMS TST_SQRS );
    foreach my $idx ( 0..5 ){
        if( $item_invalid[ $idx ] ){
            $rec_hash->{ $keys_list[ $idx ] } = undef
        }
    }
    push @tsr_rec_collection, $rec_hash;
}

sub handle_dtr( $rec_hash ){
    push @DTR_collection, $rec_hash->{ TEXT_DAT };
}

sub format_time( $time_t ){
    my ($sec,$min,$hour,$mday,$mon,$year_off,$wday,$yday,$isdat) = localtime $time_t;
    return sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $year_off + 1900, $mon + 1, $mday, $hour, $min, $sec );
}

sub output_as_tsv( $out_fh, $bin_only = 0, $chipid_fill = 1 ) {
    my @col_titles = qw( PART_TYP FACIL_ID JOB_NAM TEST_COD FLOW_ID TST_TEMP CARD_ID HAND_ID SETUP_TIME START_TIME FINISH_TIME SBLOT_ID LOT_ID WAFER_ID DIEX DIEY TEST_SEQ TD_INDEX TEST_T PART_ID HEAD_NUM SITE_NUM HARD_BIN SOFT_BIN PF HBIN_NAME SBIN_NAME );
    my $non_para_col_count = scalar @col_titles;
    my @par_list = sort { $a->{LIST_INDEX} <=> $b->{LIST_INDEX} } values %$test_parameters;
    if( not $bin_only ){
        push @col_titles, map { $_->{ TEST_NUM } . '_' .$_->{PAR_NAME} } @par_list;
    }
    # output csv title
    say $out_fh join "\t", @col_titles;

    # output parameter information;
    if( not $bin_only ){
        foreach my $info_key ( qw( TEST_NUM PAR_NAME VECT_NAM LO_SPEC HI_SPEC UNITS SCALE )){
            say $out_fh join "\t", '#', ('') x ( $non_para_col_count - 2 ),
                        $info_key,
                        map { $_->{ $info_key } // '' } @par_list;
        }
    }
    
    # my ( $wafer_seq, $diex_seq, $diey_seq ) = get_chip_id_test_sequence();

    my $lot_id = $test_information->{ LOT_ID };
    my $sb_lot_id = $test_information->{ SBLOT_ID } // '';
    my $start_time = format_time( $test_information->{ START_T  } // 0 );
    my $setup_time = format_time( $test_information->{ SETUP_T  } // 0 );
    my $end_time   = format_time( $test_information->{ FINISH_T } // 0 );
    my $JOB_NAM    = $test_information->{ JOB_NAM  };
    my $facial_id  = $test_information->{ FACIL_ID };
    my $part_type  = $test_information->{ PART_TYP };
    my $test_code  = $test_information->{ TEST_COD };
    my $flow_id    = $test_information->{ FLOW_ID  };
    my $test_temp  = $test_information->{ TST_TEMP };
    my $card_id    = $test_information->{ CARD_ID  };
    my $handler    = $test_information->{ HAND_ID  };

    if( $chipid_fill and $test_code =~ /FT/ismx ){
        fill_the_die_id();
    }

    my $test_info_line = join "\t", ( $part_type, $facial_id, $JOB_NAM, $test_code, $flow_id, $test_temp, $card_id, $handler, $setup_time, $start_time, $end_time, $sb_lot_id, $lot_id, );

    # foreach my $die_info ( values %$die_data_collection ){
    foreach my $die_seq ( sort { $a <=> $b } keys %$die_data_collection ){
        my $die_info = $die_data_collection->{ $die_seq };
        print $out_fh $test_info_line;
        print $out_fh "\t";

        my ( $hbin, $sbin ) = @$die_info{ qw( HARD_BIN SOFT_BIN ) };
        my $hbin_def = $hbin_defination{ $hbin // -1000 } // { HBIN_PF => '', HBIN_NAME => '' };
        my $sbin_def = $sbin_defination{ $sbin // -1000 } // { SBIN_PF => '', SBIN_NAME => '' };

        if( $hbin_def->{ HBIN_NAME } eq '' ){
            $hbin_def->{ HBIN_NAME } = "HBIN_$hbin";
        }

        if( $sbin_def->{ SBIN_NAME } eq '' ){
            $sbin_def->{ SBIN_NAME } = "SBIN_$sbin";
        }

        if( not $hbin_def->{ HBIN_PF } =~ /[A-Z]+/){
            $hbin_def->{ HBIN_PF } = $hbin == 1 ? 'P' : 'F';
        }

        print $out_fh join "\t", map { $_ // '' } (
                                            @$die_info{ qw( WAFER_ID DIEX DIEY TEST_SEQ TD_INDEX TEST_T PART_ID HEAD_NUM SITE_NUM HARD_BIN SOFT_BIN ) },
                                            @$hbin_def{ qw( HBIN_PF HBIN_NAME ) },
                                            $sbin_def->{ SBIN_NAME }
                                                );
        if( not $bin_only ){
            print $out_fh "\t";
            say $out_fh join "\t", map { $_ // '' } @{ $die_info->{RESULT} };
        }else{
            say $out_fh '';
        }
    }
}

sub output_as_pf_only( $out_fh, $chipid_fill = 1 ) {
    my @col_titles = qw( PART_TYP FACIL_ID JOB_NAM TEST_COD FLOW_ID TST_TEMP CARD_ID HAND_ID SETUP_TIME START_TIME FINISH_TIME LOT_ID WAFER_ID DIEX DIEY TEST_SEQ TD_INDEX TEST_T PART_ID HEAD_NUM SITE_NUM HARD_BIN SOFT_BIN PF HBIN_NAME SBIN_NAME );
    my $non_para_col_count = scalar @col_titles;
    my @par_list = sort { $a->{LIST_INDEX} <=> $b->{LIST_INDEX} } values %$test_parameters;
    
    push @col_titles, map { $_->{ TEST_NUM } . '_' .$_->{PAR_NAME} } @par_list;
    # output csv title
    say $out_fh join "\t", @col_titles;

    # output parameter information;
    foreach my $info_key ( qw( TEST_NUM PAR_NAME VECT_NAM LO_SPEC HI_SPEC UNITS SCALE )){
        say $out_fh join "\t", '#', ('') x ( $non_para_col_count - 2 ),
                    $info_key,
                    map { $_->{ $info_key } // '' } @par_list;
    }
    
    # my ( $wafer_seq, $diex_seq, $diey_seq ) = get_chip_id_test_sequence();

    my $lot_id = $test_information->{ LOT_ID };
    my $start_time = format_time( $test_information->{ START_T  } // 0 );
    my $setup_time = format_time( $test_information->{ SETUP_T  } // 0 );
    my $end_time   = format_time( $test_information->{ FINISH_T } // 0 );
    my $JOB_NAM    = $test_information->{ JOB_NAM  };
    my $facial_id  = $test_information->{ FACIL_ID };
    my $part_type  = $test_information->{ PART_TYP };
    my $test_code  = $test_information->{ TEST_COD };
    my $flow_id    = $test_information->{ FLOW_ID  };
    my $test_temp  = $test_information->{ TST_TEMP };
    my $card_id    = $test_information->{ CARD_ID  };
    my $handler    = $test_information->{ HAND_ID  };

    if( $chipid_fill and $test_code =~ /FT/ismx ){
        fill_the_die_id();
    }

    my $test_info_line = join "\t", ( $part_type, $facial_id, $JOB_NAM, $test_code, $flow_id, $test_temp, $card_id, $handler, $setup_time, $start_time, $end_time, $lot_id, );

    # foreach my $die_info ( values %$die_data_collection ){
    foreach my $die_seq ( sort { $a <=> $b } keys %$die_data_collection ){
        my $die_info = $die_data_collection->{ $die_seq };
        print $out_fh $test_info_line;
        print $out_fh "\t";

        my ( $hbin, $sbin ) = @$die_info{ qw( HARD_BIN SOFT_BIN ) };
        my $hbin_def = $hbin_defination{ $hbin // -1000 } // { HBIN_PF => '', HBIN_NAME => '' };
        my $sbin_def = $sbin_defination{ $sbin // -1000 } // { SBIN_PF => '', SBIN_NAME => '' };

        if( $hbin_def->{ HBIN_NAME } eq '' ){
            $hbin_def->{ HBIN_NAME } = "HBIN_$hbin";
        }

        if( $sbin_def->{ SBIN_NAME } eq '' ){
            $sbin_def->{ SBIN_NAME } = "SBIN_$sbin";
        }

        if( not $hbin_def->{ HBIN_PF } =~ /[A-Z]+/){
            $hbin_def->{ HBIN_PF } = $hbin == 1 ? 'P' : 'F';
        }

        print $out_fh join "\t", map { $_ // '' } (
                                            @$die_info{ qw( WAFER_ID DIEX DIEY TEST_SEQ TD_INDEX TEST_T PART_ID HEAD_NUM SITE_NUM HARD_BIN SOFT_BIN ) },
                                            @$hbin_def{ qw( HBIN_PF HBIN_NAME ) },
                                            $sbin_def->{ SBIN_NAME }
                                                );
        print $out_fh "\t";
        say $out_fh join "\t", map { $_ // '' } @{ $die_info->{PF} };
    }
}

sub fill_the_die_id(){
    my ( $wafer_seq, $diex_seq, $diey_seq ) = get_chip_id_test_sequence();

    foreach my $die_info ( values %$die_data_collection ){
        $die_info->{ WAFER_ID } = last_value { defined $_ } map { $die_info->{RESULT}->[ $_ ] } $wafer_seq->@*;
        $die_info->{ DIEX     } = last_value { defined $_ } map { $die_info->{RESULT}->[ $_ ] } $diex_seq->@*;
        $die_info->{ DIEY     } = last_value { defined $_ } map { $die_info->{RESULT}->[ $_ ] } $diey_seq->@*;
    }
}

sub get_chip_id_test_sequence(){
    die "no die data before to check chip id test sequence" if not %$test_parameters;
    my ( $wafer, $diex, $diey ) = ( [], [], [] );
    foreach my $par ( values %$test_parameters ){
        my $seq = $par->{ LIST_INDEX };
        given( $par->{PAR_NAME} ){
            when( /Chipid_x\/chipid_minutes|CHIP_ID_Read.+x-location|x_cor\s+|Efuse_X/imsx  ){ push $diex->@*, $seq  }
            when( /Chipid_y\/chipid_second|CHIP_ID_Read.+y-location|y_cor\s+|Efuse_Y\b/ismx   ){ push $diey->@*, $seq  }
            when( /Chipid_wafer\/chipid_hour|CHIP_ID_Read.+wafer_id|wafer\s+id\s+|Efuse_WaferID/ismx   ){ push $wafer->@*, $seq }
            default {}
        }
    }

    return ( $wafer, $diex, $diey );
}

sub output_tsr_collection( $tsr_fh ){
    my @keys_list = qw( TEST_NUM TEST_NAME EXEC_CNT FAL_CNT TEST_TIM SEQ_NAME TEST_MIN TEST_MAX TST_SUMS TST_SQRS );
    say $tsr_fh join "\t", @keys_list;
    foreach my $tsr ( @tsr_rec_collection ){
        say $tsr_fh join "\t", map { $_ // '' } @$tsr{ @keys_list };
    }
}

sub output_diag_log( $diag_fh ){
    foreach my $str_msg ( @DTR_collection ){
        chomp( $str_msg );
        say $diag_fh $str_msg;
    }
}

sub output_pin_map( $pin_fh ){
    say $pin_fh join "\t", qw( SITE_NUM CHAN_NAME PIN_NAME PMR_INDEX );
    while( my ( $chan_name, $v ) = each $pin_map->%* ){
        say $pin_fh join "\t",  $v->{ SITE_NUM },
                                $chan_name,
                                $v->{ LOG_NAM  } // '',
                                $v->{ PMR_INDX };
    }
}

sub output_par_spec( $spec_oh ){
    say $spec_oh join "\t", qw( TEST_NUM TEST_NAME UNIT SCALE LO_SPEC HI_SPEC );
    
    foreach my $test_name ( sort {$a cmp $b} keys $test_parameters->%* ) {
        my $par_hash = $test_parameters->{ $test_name };
        next unless defined $par_hash->{ PAR_NAME };
        # say Dumper( $test_name ) and next unless defined $par_hash->{ PAR_NAME };
        next if $par_hash->{ PAR_NAME } =~ /FUNC_/ismx;
        say $spec_oh join "\t", 
        @$par_hash{ qw( TEST_NUM PAR_NAME UNITS SCALE ) },
            @$par_hash{ qw( LO_SPEC HI_SPEC ) };
    }
}

my $param_hash_ref=get_argv_hash();

open my $oh, '>', $param_hash_ref->{'output_file'} or confess "Fail to open output file: $!";
open my $bin_oh, '>', $param_hash_ref->{'output_file'} . "_bin_only.txt" or confess "Fail to open output file: $!";
open my $dig_oh, '>', $param_hash_ref->{'output_file'} . "_diag_log.txt" or confess "Fail to open output diag log file: $!";
open my $spec_oh, '>', $param_hash_ref->{'output_file'} . "_par_spec.txt" or confess "Fail to open output parameter spec file: $!";

say sprintf("Gonna parse %s, then output to %s",
    @{$param_hash_ref}{'input_file','output_file'},
);

say "Start Time is ",scalar localtime;
my $stdf_handle=STDF_inline_C->new(
    input_file=>$param_hash_ref->{'input_file'},
    gz_flag=>1,
);

while( my $rec_header = $stdf_handle->get_rec_header ){
    my $rec_type = $rec_header->{'REC_TYPE'};
    my $rec_body = $stdf_handle->get_rec_body( $rec_header );
    if( not $rec_type ~~ [ 'MIR', 'SDR', 'PMR', 'PTR', 'FTR', 'BPS', 'PIR', 'PRR', 'WIR', 'HBR', 'SBR', 'MPR', 'MRR', 'DTR' ] ){
        next;
    }
    my $result=$stdf_handle->get_record( $rec_header, $rec_body->{'REC_BODY'} );
    given( $rec_type ){
        when('MIR'){
            handle_mir( $result->{ body } );
            my $input_file_name = $param_hash_ref->{'input_file'};
            if( ( not $test_information->{ TEST_COD } ) and $input_file_name =~ /_(FT\d*)_/ismx ){
                $test_information->{ TEST_COD } = $1;
            }
            if( not $test_information->{ FLOW_ID } ){
                if( $input_file_name =~ /[._](R[TPE]\d*)[._]/ismx ){
                    $test_information->{ FLOW_ID } = $1;
                }else{
                    $test_information->{ FLOW_ID } = 'RT0';
                }                
            }
            if( ( not $test_information->{ SBLOT_ID } ) and $input_file_name =~ /(FTM.+?|ZMA.+?|LTC.+?|HOA.+?)_/ismx ){
                $test_information->{ SBLOT_ID } = $1;
            }
            if( not $test_information->{ LOT_ID } ){
                if( $input_file_name =~ /(A[AF][NPT].+)\b/ismx ){
                    $test_information->{ LOT_ID } = $1;
                }elsif( $test_information->{ SBLOT_ID } ){
                    $test_information->{ LOT_ID } = $test_information->{ SBLOT_ID }
                }                
            }
        }
        when('SDR'){ handle_sdr( $result->{ body } ); }
        when('PMR'){ handle_pmr( $result->{ body } ); }
        when('PTR'){ handle_ptr( $result->{ body } ); }
        when('FTR'){ handle_ftr( $result->{ body } ); }
        when(/BPS|PIR/ismx){ handle_bps( $result->{ body } ); }
        when('PRR'){ handle_prr( $result->{ body } ); }
        when('WIR'){ handle_wir( $result->{ body } ); }
        when('HBR'){ handle_hbr( $result->{ body } ); }
        when('SBR'){ handle_sbr( $result->{ body } ); }
        when('TSR'){ handle_tsr( $result->{ body } ); }
        when('MPR'){ handle_mpr( $result->{ body } ); }
        when('MRR'){ handle_mrr( $result->{ body } ); }
        when('DTR'){ handle_dtr( $result->{ body } ); }
        default {}
    }
}
#收尾
handle_bps(undef);

say "Ready to do output.";
output_as_tsv( $oh );
output_as_tsv( $bin_oh, 1, 1 );
output_diag_log( $dig_oh );

open my $pf_fh, '>', $param_hash_ref->{'output_file'} . "_PF_only.txt" or confess "Fail to open output PF_Only file: $!";
output_as_pf_only( $pf_fh );
close $pf_fh;

open my $pin_fh, '>', $param_hash_ref->{'output_file'} . "_pin_map.txt" or confess "Fail to open output pin_map file: $!";
output_pin_map( $pin_fh );
output_par_spec( $spec_oh );

# open my $tsr_fh, '>', $param_hash_ref->{'output_file'} . '.tsr.txt' or die "Failed to open file to output TSR record. $!";

# output_tsr_collection( $tsr_fh );

say Dumper( $test_information );
say "End Time is ",scalar localtime;